module Execute(
  input clk,
  input rst,

  input enable,
  input flush,
  output ready,
  output mul_ready,
  output div_ready,
//from decode stage
  input [31:0] inst_i,
  input [63:0] pc_i,
  input [63:0] src1,
  input [63:0] src2,
  input [63:0] imm,
  input [4:0] dest_i,
  input [4:0] rs1_i,
  input [4:0] rs2_i,

  input [6:0] arithmetic_op,
  input [21:0] logic_op,
  input [7:0] branch_op,
  input [7:0] load_op,
  input [3:0] store_op,
  input [1:0] other_op,
  input [4:0] mul_op,
  input [7:0] div_op,
  input [7:0] csr_op,
  input [0:0] fence_op,
  input [0:0] unsupport_op,

  input ex_intr,
  input mtip_clear,
  input tim_int_req,
  input msip_i,
  input msip_valid_i,
  output msip_o,


  output icache_invalid,

  output [63:0] pc_o,
  output difftest_raise_hard_intr,
//to PC
  output [31:0] inst_o,
  output [63:0] target_pc_o,
  output target_pc_valid_o,
//to memory stage
  output [1:0] other_op_o,
  output load_valid,
  output [63:0] load_addr,
  output [3:0] load_bytes,
  output load_sign,

  output store_valid,
  output [63:0] store_addr,
  output [63:0] store_data,
  output [3:0] store_bytes,

  output [63:0] mul_res,
  output [63:0] div_res,

  output [4:0] dest_o,
  output [63:0] dest_data,
  output dest_valid_o

);
  assign pc_o = pc_i;
  assign dest_o = dest_i;
  assign inst_o = inst_i;
  assign other_op_o = other_op;
  wire [11:0] csr_reg_addr_i;
  wire csr_reg_read_i;
  wire csr_reg_write_i;
  wire csr_reg_set_i;
  wire csr_reg_clear_i;
  wire [63:0] csr_reg_wdata_i;
  wire [63:0] csr_reg_rdata_o;

  wire csr_reg_raise_intr_i;
  wire csr_reg_op_mret_i;
  wire [63:0] csr_reg_mepc_rdata_o;
  wire csr_reg_mepc_write_i;
  wire [63:0] csr_reg_mepc_wdata_i;
  wire csr_reg_mcause_write_i;
  wire [63:0] csr_reg_mcause_wdata_i;
  wire [63:0] csr_reg_mtvec_rdata_o;
  wire csr_mie_rdata_o;

  wire tim_intr;

  CSRsFile inst_CSRsFile(
    .clk              (clk),
    .rst              (rst),
    .csr_addr         (csr_reg_addr_i),
    .csr_read         (csr_reg_read_i),
    .csr_write        (csr_reg_write_i),
    .csr_set          (csr_reg_set_i),
    .csr_clear        (csr_reg_clear_i),
    .csr_wdata        (csr_reg_wdata_i),
    .csr_rdata        (csr_reg_rdata_o),
    .raise_intr       (csr_reg_raise_intr_i),
    .tim_int_req      (tim_int_req),
    .mtip_clear       (mtip_clear),
    .msip_i           (msip_i),
    .msip_valid_i     (msip_valid_i),
    .msip_o           (msip_o),
    .mtip             (tim_intr),
    .op_mret          (csr_reg_op_mret_i),
    .csr_mie_rdata    (csr_mie_rdata_o),
    .csr_mepc_rdata   (csr_reg_mepc_rdata_o),
    .csr_mepc_write   (csr_reg_mepc_write_i),
    .csr_mepc_wdata   (csr_reg_mepc_wdata_i),
    .csr_mcause_write (csr_reg_mcause_write_i),
    .csr_mcause_wdata (csr_reg_mcause_wdata_i),
    .csr_mtvec_rdata  (csr_reg_mtvec_rdata_o)
  );


  wire [63:0] arithmetic_res;
  wire arithmetic_valid;
  arithmetic_unit inst_arithmetic_unit (.op(arithmetic_op), .src1(src1), .src2(src2), .imm(imm), .pc_i(pc_i), .res(arithmetic_res), .valid_o(arithmetic_valid));

  wire [63:0] logic_res;
  wire logic_valid;
  logic_unit inst_logic_unit (.op(logic_op), .src1(src1), .src2(src2), .imm(imm), .pc_i(pc_i), .res(logic_res), .valid_o(logic_valid));

  wire [63:0] branch_res;
  wire branch_valid;
  wire [63:0] branch_pc_o;
  wire branch_pc_valid_o;
  branch_unit inst_branch_unit(
      .op         (branch_op),
      .src1       (src1),
      .src2       (src2),
      .imm        (imm),
      .pc_i       (pc_i),
      .pc_o       (branch_pc_o),
      .pc_valid_o (branch_pc_valid_o),
      .res        (branch_res),
      .valid_o    (branch_valid)
    );

  wire load_dest_valid;
  wire [63:0] load_res;
  load_unit inst_load_unit(
      .op      ({8{(~difftest_raise_hard_intr)}}&load_op),
      .src1    (src1),
      .src2    (src2),
      .imm     (imm),
      .sign    (load_sign),
      .bytes   (load_bytes),
      .addr    (load_addr),
      .ren     (load_valid),
      .res     (load_res),
      .valid_o (load_dest_valid)
    );  

  store_unit inst_store_unit(
      .op      (store_op),
      .src1    (src1),
      .src2    (src2),
      .imm     (imm),
      .addr    (store_addr),
      .data    (store_data),
      .bytes   (store_bytes),
      .valid_o (store_valid)
    );
  // wire [63:0] mul_res;
  wire mul_valid;
  // wire mul_ready;
  mul_unit inst_mul_unit(
      .clk       (clk),
      .rst       (rst),
      .flush     (flush),
      .op        (mul_op),
      .src1      (src1),
      .src2      (src2),
      .imm       (imm),
      .ready     (mul_ready),
      .res       (mul_res),
      .valid_o   (mul_valid)
    );
  // wire [63:0] div_res;
  wire div_valid;
  // wire div_ready;
  div_unit inst_div_unit(
      .clk       (clk),
      .rst       (rst),
      .flush     (flush),
      .op        (div_op),
      .src1      (src1),
      .src2      (src2),
      .imm       (imm),
      .ready     (div_ready),
      .res       (div_res),
      .valid_o   (div_valid)
    );

  wire [63:0] csr_res;
  wire csr_valid;
  wire [63:0] csr_pc_o;
  wire csr_pc_valid_o;
  csr_unit inst_csr_unit(
      .enable           (enable),
      .op               (csr_op),
      .src1             (src1),
      .src2             (src2),
      .imm              (imm),
      .pc_i             (pc_i),
      .dest             (dest_i),
      .rs1              (rs1_i),
      .csr_addr         (csr_reg_addr_i),
      .csr_read         (csr_reg_read_i),
      .csr_write        (csr_reg_write_i),
      .csr_set          (csr_reg_set_i),
      .csr_clear        (csr_reg_clear_i),
      .csr_wdata        (csr_reg_wdata_i),
      .csr_rdata        (csr_reg_rdata_o),
      .unsupport_op     (unsupport_op),
      .ex_intr          ((ex_intr|tim_intr)),
      .raise_intr       (csr_reg_raise_intr_i),
      .raise_hard_intr  (difftest_raise_hard_intr),
      .op_mret          (csr_reg_op_mret_i),
      .csr_mie_rdata    (csr_mie_rdata_o),
      .csr_mepc_rdata   (csr_reg_mepc_rdata_o),
      .csr_mepc_write   (csr_reg_mepc_write_i),
      .csr_mepc_wdata   (csr_reg_mepc_wdata_i),
      .csr_mcause_write (csr_reg_mcause_write_i),
      .csr_mcause_wdata (csr_reg_mcause_wdata_i),
      .csr_mtvec_rdata  (csr_reg_mtvec_rdata_o),
      .pc_o             (csr_pc_o),
      .pc_valid_o       (csr_pc_valid_o),
      .res              (csr_res),
      .valid_o          (csr_valid)
    );

  assign dest_valid_o = (~difftest_raise_hard_intr)&(branch_valid | arithmetic_valid | logic_valid | load_dest_valid | mul_valid | div_valid | csr_valid);

  assign dest_data = arithmetic_res | logic_res | branch_res | load_res | csr_res;

  wire branch_op_pc_valid = (~difftest_raise_hard_intr)&branch_pc_valid_o;
  assign target_pc_valid_o = branch_op_pc_valid | csr_pc_valid_o;
  assign target_pc_o = {64{branch_op_pc_valid}}&branch_pc_o | {64{(~branch_op_pc_valid)&csr_pc_valid_o}}&csr_pc_o;

  assign icache_invalid = enable&fence_op[0];

  assign ready = mul_ready & div_ready & enable;
endmodule
